#ifndef EVENTMANAGER_H
#define EVENTMANAGER_H

/* Copyright (C) 2011 Riccardo Marson

   This file is part of OpenPool, an open-source game of pool. 

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   RICCARDO MARSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
   AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  See Readme.rtf for further information.
  
*/

#include "interfaces.h"
#include "events.h"
#include "logmanager.h"

//This event-based system is largely based on the book "Game Coding Complete, 3rd Edition"
//and the Qt SDK documentation

/*global typedefs, visible by all other subsystems including eventmanager.h
boost::shared_ptr is needed here beacause:
- For the event system to work in an elegant way, the events must be queued
    as pointers to interfaces
- Events are created elsewhere in local scopes and in the majority of cases
    their pointers go out of scope long before the queue is processed
- boost::shared_ptr allows to store these references in a safe way until they are
    processed and ensures they get desroyed only when they are no longer needed
- The same is true for the lists of listeners. If a pointer to an instance
    of a listener which "lives" elsewhere is simply added to the list it cannot be
    removed or the object's destructor will be called. the reference counting feature
    of boost::shared_ptr allows multiple objetcs to point to the same instance without
    the risk of one of them destroying it prematurely.
*/

class CBaseEventListener : public IEventListener
{
public:

    CBaseEventListener(string name);
    virtual ~CBaseEventListener();

    virtual string const getName();
    virtual bool processEvent(EventPtr event);

protected:

    string mName;

};

//the event manager is a singleton within the application for easy access
//anywhere.
class CEventManager
{
public:

    CEventManager();
    virtual ~CEventManager();

    static CEventManager* getSingleton();

    //queue an event for later (asyncronous) processing. Used for the
    //majoruty of messages.
    void queueEvent(EventPtr event);
    //send and event instantanously (sincronous processing).
    //useful for urgent messages.
    bool sendEvent(EventPtr event);
    //register a listener to a certain type of event. That listener will be notified
    //each time an event of that type is raised.
    bool registerListener(eEventType eventType, EventListenerPtr listener);
    //a listener is tied to a game subsystem, therefore it's unregistered only when the subsystem
    //is shutting down and hence it gets unsuscribed to all the events it was listening for.
    bool unregisterListener(EventListenerPtr listener);
    //sends all the event queued for processing in the prevoius loop.
    //this function gets called by the main loop.
    void processEventQueue();
    //the time allocated to the event manager for event processing is limited.
    //this function resets the maximum time allowed.
    void resetTimeoutThreshold(float maxTime);

private:

    static CEventManager* uniqueInstance;

    //local typedefs visible only to the event manager
    typedef set< eEventType > EventTypeSet;
    typedef list< EventListenerPtr > ListenerList;
    typedef map< eEventType, ListenerList > ListenerMap;
    typedef list< EventPtr > EventQueue;

    EventTypeSet mRegisteredEvents;
    ListenerMap mRegisteredListeners;
    EventQueue mPendingEvents[2];

    float mMaxProcessingTime;

    //the event queue is double buffered to avoid an event being added while
    //the queue is being processed. newly created events go into an inactive queue
    //and are processed the next time around.
    unsigned short mActiveQueue;

    //swaps the active/inactive queues.
    inline void swapQueues();

    //broadcast a single event. called both by sendEvent() and processEventQueue()
    bool processEvent(EventPtr event);
};

inline void CEventManager::swapQueues()
{
    if (mActiveQueue == 0)
        mActiveQueue = 1;
    else
        mActiveQueue = 0;
}

#endif // EVENTMANAGER_H
